namespace SimpleWebServices.Globalization
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Threading;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.XPath;
    using SimpleWebServices;
    using Xunit;

    public sealed class TranslationCollectionFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<TranslationCollection<int>>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .XmlRoot("translations")
                .Implements<IXmlSerializable>()
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            TranslationCollection<string> obj = (
                "<translations>" +
                    "<translation><string>Invariant</string></translation>" +
                    "<translation xml:lang='en'><string>English</string></translation>" +
                    "<translation xml:lang='fr'><string>French</string></translation>" +
                "</translations>").Deserialize<TranslationCollection<string>>();

            Assert.Equal<string>("Invariant", obj[Language.Invariant]);
            Assert.Equal<string>("English", obj["en"]);
            Assert.Equal<string>("French", obj["fr"]);
        }

        [Fact]
        public void deserializeEmpty()
        {
            Assert.Equal<int>(0, "<translations />".Deserialize<TranslationCollection<int>>().Count);
        }

        [Fact]
        public void serialize()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>
            {
                { CultureInfo.InvariantCulture, "Invariant" },
                { "en", "English" },
                { "fr", "French" }
            };

            XPathNavigator navigator = obj.Serialize().CreateNavigator();
            XmlNamespaceManager namespaces = new XmlNamespaceManager(navigator.NameTable);
            namespaces.AddNamespace("xml", "http://www.w3.org/XML/1998/namespace");

            Assert.True((bool)navigator.Evaluate("1=count(/translations/translation[not(@xml:lang)]/string[text()='Invariant'])", namespaces));
            Assert.True((bool)navigator.Evaluate("1=count(/translations/translation[@xml:lang='en']/string[text()='English'])", namespaces));
            Assert.True((bool)navigator.Evaluate("1=count(/translations/translation[@xml:lang='fr']/string[text()='French'])", namespaces));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new TranslationCollection<string>());
        }

        [Fact]
        public void prop_Count()
        {
            Assert.True(new PropertyExpectations(typeof(TranslationCollection<string>).GetProperty("Count"))
                .TypeIs<int>()
                .DefaultValueIs(0)
                .Result);
        }

        [Fact]
        public void prop_Count_get()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>
            {
                { CultureInfo.InvariantCulture, "Invariant" },
                { "en", "English" },
                { "fr", "French" }
            };

            Assert.Equal<int>(3, obj.Count);
        }

        [Fact]
        public void Current_getCulture()
        {
            CultureInfo culture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

                string expected = "French, as spoken in France";

                TranslationCollection<string> obj = new TranslationCollection<string>();
                obj["en"] = "English";
                obj["fr"] = "French";
                obj["fr-FR"] = expected;

                string actual = obj.Current;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        [Fact]
        public void Current_getLanguage()
        {
            CultureInfo culture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");

                string expected = "French";

                TranslationCollection<string> obj = new TranslationCollection<string>();
                obj["en"] = "English";
                obj["fr"] = expected;

                string actual = obj.Current;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        [Fact]
        public void Current_getLanguageFallback()
        {
            CultureInfo culture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

                string expected = "French";

                TranslationCollection<string> obj = new TranslationCollection<string>();
                obj["en"] = "English";
                obj["fr"] = expected;

                string actual = obj.Current;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        [Fact]
        public void Current_getFallback()
        {
            CultureInfo culture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

                string expected = "English";

                TranslationCollection<string> obj = new TranslationCollection<string>();
                obj["en"] = expected;
                obj["de"] = "German";

                string actual = obj.Current;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        [Fact]
        public void Current_getFallbackInvariant()
        {
            CultureInfo culture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

                string expected = "Invariant";

                TranslationCollection<string> obj = new TranslationCollection<string>();
                obj["en"] = "English";
                obj["de"] = "German";
                obj[string.Empty] = expected;

                string actual = obj.Current;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                Thread.CurrentThread.CurrentUICulture = culture;
            }
        }

        [Fact]
        public void indexer_string_get()
        {
            string expected = "English";

            TranslationCollection<string> obj = new TranslationCollection<string>();
            obj["en"] = expected;

            string actual = obj["en"];

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Add_Language_T()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>();
            obj.Add("en", "English");

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void op_Add_Language_TNull()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>();
            obj.Add("en", null);

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void op_Clear()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>
            {
                { CultureInfo.InvariantCulture, "Invariant" },
                { "en", "English" },
                { "fr", "French" }
            };

            Assert.NotEmpty(obj);

            obj.Clear();
            Assert.Empty(obj);
        }

        [Fact]
        public void op_Remove_Language()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>
            {
                { Language.Invariant, "Invariant" },
                { "en", "English" },
                { "fr", "French" }
            };

            obj.Remove("en");
            Assert.Equal<int>(2, obj.Count);
        }

        [Fact]
        public void op_ToString()
        {
            TranslationCollection<string> obj = new TranslationCollection<string>
            {
                { CultureInfo.InvariantCulture, "Invariant" },
                { "en", "English" },
                { "fr", "French" }
            };

            string expected = 
                "=Invariant" + Environment.NewLine +
                "en=English" + Environment.NewLine +
                "fr=French";
            string actual = obj.ToString();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_ToString_whenEmpty()
        {
            string expected = string.Empty;
            string actual = new TranslationCollection<string>().ToString();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void IEnumerable_op_GetEnumerator()
        {
            Assert.NotNull((new TranslationCollection<int>() as IEnumerable).GetEnumerator());
        }

        [Fact]
        public void IEnumerableOfT_op_GetEnumerator()
        {
            Assert.NotNull((new TranslationCollection<int>() as IEnumerable<int>).GetEnumerator());
        }

        [Fact]
        public void IXmlSerializable_op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new TranslationCollection<int>() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void IXmlSerializable_op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new TranslationCollection<int>() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void IXmlSerializable_op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new TranslationCollection<int>() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}